package com.example.unruly_zhao.mopaide.mqtt;

import android.app.AlarmManager;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.IBinder;
import android.os.StrictMode;
import android.util.Log;

import com.ibm.mqtt.IMqttClient;
import com.ibm.mqtt.MqttClient;
import com.ibm.mqtt.MqttException;
import com.ibm.mqtt.MqttPersistence;
import com.ibm.mqtt.MqttPersistenceException;
import com.ibm.mqtt.MqttSimpleCallback;
import com.orhanobut.logger.AndroidLogAdapter;
import com.orhanobut.logger.Logger;

import java.io.IOException;

/*
 * PushService that does all of the work.
 * Most of the logic is borrowed from KeepAliveService.
 * http://code.google.com/p/android-random/source/browse/trunk/TestKeepAlive/src/org/devtcg/demo/keepalive/KeepAliveService.java?r=219
 */
public class PushService extends Service {
    // this is the log tag
    // 这是日志标签
    public static final String TAG = "DemoPushService";
    // the IP address, where your MQTT broker is running.
    // 您的MQTT代理正在运行的IP地址
    private static final String MQTT_HOST = "119.29.108.115";
    // the port at which the broker is running.
    // 代理正在运行。
    private static int MQTT_BROKER_PORT_NUM = 1883;
    // Let's not use the MQTT persistence.
    // 我们不使用MQTT持久性
    private static MqttPersistence MQTT_PERSISTENCE = null;
    // We don't need to remember any state between the connections, so we use a clean start.
    // 我们不需要记住连接之间的任何状态，所以我们使用一个干净的开始。
    private static boolean MQTT_CLEAN_START = true;
    // Let's set the internal keep alive for MQTT to 15 mins. I haven't tested this value much. It could probably be increased.
    // 让我们将MQTT的内部保存时间设置为15分钟。我没有测试过这个值。它很可能会增加。
    private static short MQTT_KEEP_ALIVE = (short) (60 * 15);
    // Set quality of services to 0 (at most once delivery), since we don't want push notifications
    // arrive more than once. However, this means that some messages might get lost (delivery is not guaranteed)
    // 将服务质量设置为0(最多一次发送)，因为我们不希望推送通知
    //到达不止一次。然而，这意味着有些消息可能会丢失(交付无法保证)
    private static int[] MQTT_QUALITIES_OF_SERVICE = {0};
    private static int MQTT_QUALITY_OF_SERVICE = 0;
    // The broker should not retain any messages.
    // 代理不应该保留任何消息。
    private static boolean MQTT_RETAINED_PUBLISH = false;

    // MQTT client ID, which is given the broker. In this example, I also use this for the topic header.
    // You can use this to run push notifications for multiple apps with one MQTT broker.
    // MQTT客户机ID，它被授予代理。在这个例子中，我也将它用于主题标题。
    //你可以用这个来运行一个MQTT代理的多个应用程序的推送通知。

    public static String MQTT_CLIENT_ID = "tokudu";

    // These are the actions for the service (name are descriptive enough)
    // 这些是服务的操作(名称是描述性的)
    private static final String ACTION_START = MQTT_CLIENT_ID + ".START";
    private static final String ACTION_STOP = MQTT_CLIENT_ID + ".STOP";
    private static final String ACTION_KEEPALIVE = MQTT_CLIENT_ID + ".KEEP_ALIVE";
    private static final String ACTION_RECONNECT = MQTT_CLIENT_ID + ".RECONNECT";

    // Connection log for the push service. Good for debugging.

    // 用于推送服务的连接日志。对于调试好。
    private ConnectionLog mLog;

    // Connectivity manager to determining, when the phone loses connection
    // 连接管理器来确定，当手机失去连接时
    private ConnectivityManager mConnMan;
    // Notification manager to displaying arrived push notifications
    // 通知管理器显示到达的推送通知
    private NotificationManager mNotifMan;

    // Whether or not the service has been started.
    // 服务是否已经启动。
    private boolean mStarted;

    // This the application level keep-alive interval, that is used by the AlarmManager
    // to keep the connection active, even when the device goes to sleep.
    // 这是警报管理器使用的应用程序级别的保持-存活时间间隔
    //使连接保持活跃，即使是在设备进入睡眠状态时。
    private static final long KEEP_ALIVE_INTERVAL = 1000 ;

    // Retry intervals, when the connection is lost.
    // 当连接丢失时，重试间隔。
    private static final long INITIAL_RETRY_INTERVAL = 1000 * 10;
    private static final long MAXIMUM_RETRY_INTERVAL = 1000 * 60 * 30;

    // Preferences instance
    // 偏好实例
    private SharedPreferences mPrefs;
    // We store in the preferences, whether or not the service has been started
    // 不管服务是否已经启动，我们都存储在首选项中
    public static final String PREF_STARTED = "isStarted";
    // We also store the deviceID (target)
    // 我们还存储设备eid(目标)
    public static final String PREF_DEVICE_ID = "deviceID";
    // We store the last retry interval
    // 我们存储最后一个重试间隔
    public static final String PREF_RETRY = "retryInterval";

    // Notification title
    // 通知标题
    public static String NOTIF_TITLE = "Tokudu";
    // Notification id
    // 通知id
    private static final int NOTIF_CONNECTED = 0;

    // This is the instance of an MQTT connection.
    // MQTT连接的实例。
    private MQTTConnection mConnection;
    private long mStartTime;
    private String mDeviceID;


    // Static method to start the service
    // 启动服务的tatic方法
    public static void actionStart(Context ctx) {
        Logger.i("actionStart");
        Intent i = new Intent(ctx, PushService.class);
        i.setAction(ACTION_START);
        ctx.startService(i);
    }

    // Static method to stop the service
    // 停止服务的静态方法
    public static void actionStop(Context ctx) {
        Logger.i("actionStop");
        Intent i = new Intent(ctx, PushService.class);
        i.setAction(ACTION_STOP);
        Log.e("eeee","actionStop");
        ctx.startService(i);
    }

    // Static method to send a keep alive message
    // 静态方法发送一个保存的消息
    public static void actionPing(Context ctx) {
        Intent i = new Intent(ctx, PushService.class);
        i.setAction(ACTION_KEEPALIVE);
        ctx.startService(i);
    }

    @Override
    public void onCreate() {
        super.onCreate();
        Logger.i("onCreate");

        log("Creating service");
        mStartTime = System.currentTimeMillis();

        try {

            mLog = new ConnectionLog();
            Log.i(TAG, "Opened log at " + mLog.getPath());

        } catch (IOException e) {

            Log.e(TAG, "Failed to open log", e);

        }

        // Get instances of preferences, connectivity manager and notification manager
        // 获得首选项、连接管理器和通知管理器实例
        mPrefs = getSharedPreferences(TAG, MODE_PRIVATE);
        mConnMan = (ConnectivityManager) getSystemService(CONNECTIVITY_SERVICE);
        mNotifMan = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);

		/* If our process was reaped by the system for any reason we need
         * to restore our state with merely a call to onCreate.  We record
		 * the last "started" value and restore it here if necessary.
		 * 如果我们的过程因为任何原因而被系统所获取
         * 通过调用onCreate来恢复我们的状态。我们记录
         * 最后一个“开始”值并在必要时恢复它。
         */
        handleCrashedService();
    }

    // This method does any necessary clean-up need in case the server has been destroyed by the system
    // and then restarted
    //他的方法做了任何必要的清理工作，以防止服务器被系统破坏。
    //然后重启
    private void handleCrashedService() {

        Logger.i("handleCrashedService");

        if (wasStarted() == true) {
            log("Handling crashed service...");
            // stop the keep alives
            stopKeepAlives();

            // Do a clean start
            start();
        }
    }

    @Override
    public void onDestroy() {

        Logger.i("onDestroy");

        log("Service destroyed (started=" + mStarted + ")");

        // Stop the services, if it has been started
        if (mStarted == true) {
            stop();
        }

        try {
            if (mLog != null)
                mLog.close();
        } catch (IOException e) {
        }
    }

    @Override
    public void onStart(Intent intent, int startId) {

        Logger.i("onStart");

        super.onStart(intent, startId);
        log("Service started with intent=" + intent);
        if (intent == null) {
            return;
        }
        // Do an appropriate action based on the intent.
        if (intent.getAction().equals(ACTION_STOP) == true) {
            stop();
            stopSelf();
        } else if (intent.getAction().equals(ACTION_START) == true) {
            start();
        } else if (intent.getAction().equals(ACTION_KEEPALIVE) == true) {
            keepAlive();
        } else if (intent.getAction().equals(ACTION_RECONNECT) == true) {
            if (isNetworkAvailable()) {
                reconnectIfNecessary();
            }
        }
    }

    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    // log helper function
    // 日志helper函数
    private void log(String message) {
        log(message, null);
    }

    private void log(String message, Throwable e) {
        if (e != null) {
            Log.e(TAG, message, e);

        } else {
            Log.i(TAG, message);
        }

        if (mLog != null) {
            try {
                mLog.println(message);
            } catch (IOException ex) {
            }
        }
    }

    // Reads whether or not the service has been started from the preferences
    // 读取服务是否已从首选项开始
    private boolean wasStarted() {

        return mPrefs.getBoolean(PREF_STARTED, false);

    }

    // Sets whether or not the services has been started in the preferences.
    // 设置服务是否已在首选项中启动。
    private void setStarted(boolean started) {

        mPrefs.edit().putBoolean(PREF_STARTED, started).commit();

        mStarted = started;
    }

    private synchronized void start() {

        Logger.i("start");

        log("Starting service...");

        // Do nothing, if the service is already running.
        // 如果服务已经在运行，什么也不要做。

        if (mStarted == true) {

            Log.w(TAG, "Attempt to start connection that is already active");

            return;

        }

        // Establish an MQTT connection
        // 建立 MQTT 连接
        connect();



        // Register a connectivity listener
        // 注册一个连接监听器
        registerReceiver(mConnectivityChanged, new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION));
    }

    private synchronized void stop() {
        // Do nothing, if the service is not running.
        // 如果服务没有运行，什么也不做。
        if (mStarted == false) {
            Log.w(TAG, "Attempt to stop connection not active.");
            return;
        }

        // Save stopped state in the preferences
        // 在首选项中保存停止状态
        setStarted(false);

        // Remove the connectivity receiver
        unregisterReceiver(mConnectivityChanged);
        // Any existing reconnect timers should be removed, since we explicitly stopping the service.
        // 任何现有的重新连接计时器都应该被删除，因为我们明确地停止了服务。
        cancelReconnect();

        // Destroy the MQTT connection if there is one
        // 如果有一个，则销毁MQTT连接
        if (mConnection != null) {
            mConnection.disconnect();
            mConnection = null;
        }
    }

    //
    private synchronized void connect() {
        log("Connecting...");

        Logger.i("connect");

        // fetch the device ID from the preferences.
        // 从首选项获取设备ID。
        mDeviceID = mPrefs.getString(PREF_DEVICE_ID, null);

        Logger.i("喜欢你都那么久那么久了"+mDeviceID);

        //仅在设备id不是空的情况下创建一个新的连接
        // Create a new connection only if the device id is not NULL
        if (mDeviceID == null) {
            log("Device ID not found.");
        } else {
            try {
                mConnection = new MQTTConnection(MQTT_HOST, mDeviceID);

                Logger.i("喜欢你都那么久那么久了"+mConnection+"仅在设备id不是空的情况下创建一个新的连接");

            } catch (MqttException e) {
                // Schedule a reconnect, if we failed to connect
                // 如果我们没有连接，就安排重新连接
                log("MqttException: " + (e.getMessage() != null ? e.getMessage() : "NULL"));

                Logger.i("MqttException: " + (e.getMessage() != null ? e.getMessage() : "NULL"));

                if (isNetworkAvailable()) {
                    scheduleReconnect(mStartTime);
                }
            }
            setStarted(true);
        }
    }

    private synchronized void keepAlive() {

        Logger.i("keepAlive");
        try {
            // Send a keep alive, if there is a connection.
            // 如果有连接，请发送一个保存。
            if (mStarted == true && mConnection != null) {
                mConnection.sendKeepAlive();
            }
        } catch (MqttException e) {

            log("MqttException: " + (e.getMessage() != null ? e.getMessage() : "NULL"), e);

            Logger.i("MqttException: " + (e.getMessage() != null ? e.getMessage() : "NULL"),e);

            mConnection.disconnect();
            mConnection = null;
            cancelReconnect();

            Logger.i("keepAlive");
        }
    }

    // Schedule application level keep-alives using the AlarmManager
    // 使用警报管理器来调度应用程序级别的保持
    private void startKeepAlives() {
        Intent i = new Intent();
        i.setClass(this, PushService.class);
        i.setAction(ACTION_KEEPALIVE);
        PendingIntent pi = PendingIntent.getService(this, 0, i, 0);
        AlarmManager alarmMgr = (AlarmManager) getSystemService(ALARM_SERVICE);
        alarmMgr.setRepeating(AlarmManager.RTC_WAKEUP,
                System.currentTimeMillis() + KEEP_ALIVE_INTERVAL,
                KEEP_ALIVE_INTERVAL, pi);
    }

    // Remove all scheduled keep alives
    // 删除所有预定的保存时间
    private void stopKeepAlives() {
        Intent i = new Intent();
        i.setClass(this, PushService.class);
        i.setAction(ACTION_KEEPALIVE);
        PendingIntent pi = PendingIntent.getService(this, 0, i, 0);
        AlarmManager alarmMgr = (AlarmManager) getSystemService(ALARM_SERVICE);
        alarmMgr.cancel(pi);
        Logger.i("stopKeepAlives");
    }

    // We schedule a reconnect based on the starttime of the service
    // 我们根据服务的起始时间安排重新连接

    public void scheduleReconnect(long startTime) {
        // the last keep-alive interval
        long interval = mPrefs.getLong(PREF_RETRY, INITIAL_RETRY_INTERVAL);

        // Calculate the elapsed time since the start
        // 计算从开始时的运行时间
        long now = System.currentTimeMillis();
        long elapsed = now - startTime;


        // Set an appropriate interval based on the elapsed time since start
        // 根据开始时的运行时间设置适当的间隔

        if (elapsed < interval) {
            interval = Math.min(interval * 4, MAXIMUM_RETRY_INTERVAL);
        } else {
            interval = INITIAL_RETRY_INTERVAL;
        }

        log("Rescheduling connection in " + interval + "ms.");

        // Save the new internval
        // 保存新internval

        mPrefs.edit().putLong(PREF_RETRY, interval).commit();

        // Schedule a reconnect using the alarm manager.
        // 使用警报管理器安排重新连接。
        Intent i = new Intent();
        i.setClass(this, PushService.class);
        i.setAction(ACTION_RECONNECT);
        PendingIntent pi = PendingIntent.getService(this, 0, i, 0);
        AlarmManager alarmMgr = (AlarmManager) getSystemService(ALARM_SERVICE);
        alarmMgr.set(AlarmManager.RTC_WAKEUP, now + interval, pi);
    }

    // Remove the scheduled reconnect
    // 删除计划重新连接
    public void cancelReconnect() {
        Intent i = new Intent();
        i.setClass(this, PushService.class);
        i.setAction(ACTION_RECONNECT);
        PendingIntent pi = PendingIntent.getService(this, 0, i, 0);
        AlarmManager alarmMgr = (AlarmManager) getSystemService(ALARM_SERVICE);
        alarmMgr.cancel(pi);
    }

    private synchronized void reconnectIfNecessary() {
        if (mStarted == true && mConnection == null) {
            log("Reconnecting...");
            connect();
        }
    }

    // This receiver listeners for network changes and updates the MQTT connection
    // accordingly
    //这个接收器侦听器用于网络更改和更新MQTT连接
    //相应的
    private BroadcastReceiver mConnectivityChanged = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            // Get network info
            NetworkInfo info = (NetworkInfo) intent.getParcelableExtra(ConnectivityManager.EXTRA_NETWORK_INFO);

            // Is there connectivity?
            // 有连接吗?
            boolean hasConnectivity = (info != null && info.isConnected()) ? true : false;

            log("Connectivity changed: connected=" + hasConnectivity);

            if (hasConnectivity) {
                reconnectIfNecessary();
            } else if (mConnection != null) {
                // if there no connectivity, make sure MQTT connection is destroyed
                mConnection.disconnect();
                cancelReconnect();
                mConnection = null;
            }
        }
    };

    // Display the topbar notification
    // 显示topbar通知
    private void showNotification(String text) {

        Intent intent = new Intent("zzq");  //为BroadcastReceiver指定action，使之用于接收同action的广播
        intent.putExtra("msg", text);
        PushService.this.sendBroadcast(intent);

        Logger.addLogAdapter(new AndroidLogAdapter());

        Logger.d("msg");

     /* String url = "unlock";
        if (url.equals(text)){
            Intent intent = new Intent(PushService.this, Activity_Menu.class);
            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); // Service跳转到Activity 要加这个标记
            startActivity(intent);
        }*/
    }

    // Check if we are online
    // 检查我们是否在线
    private boolean isNetworkAvailable() {
        NetworkInfo info = mConnMan.getActiveNetworkInfo();
        if (info == null) {
            return false;
        }
        return info.isConnected();
    }

    // This inner class is a wrapper on top of MQTT client.
    // 这个内部类是MQTT客户机上的一个包装器。
    private class MQTTConnection implements MqttSimpleCallback {
        IMqttClient mqttClient = null;

        // Creates a new connection given the broker address and initial topic
        // 创建一个新的连接，给定代理地址和初始主题
        public MQTTConnection(String brokerHostName, String initTopic) throws MqttException {
            // Create connection spec
            // 创建连接规范
            String mqttConnSpec = "tcp://" + brokerHostName + "@" + MQTT_BROKER_PORT_NUM;
            // Create the client and connect
            // 创建客户端和连接
            mqttClient = MqttClient.createMqttClient(mqttConnSpec, MQTT_PERSISTENCE);
            String clientID = MQTT_CLIENT_ID + "/" + mPrefs.getString(PREF_DEVICE_ID, "");


            if (android.os.Build.VERSION.SDK_INT > 9) {
                StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder()
                        .permitAll().build();
                StrictMode.setThreadPolicy(policy);
            }

            mqttClient.connect(clientID, MQTT_CLEAN_START, MQTT_KEEP_ALIVE);

            // register this client app has being able to receive messages
            // 注册这个客户端应用程序可以接收消息
            mqttClient.registerSimpleHandler(this);

            // Subscribe to an initial topic, which is combination of client ID and device ID.
            // 订阅一个初始主题，该主题是客户ID和设备ID的组合。
            initTopic = MQTT_CLIENT_ID + "/" + initTopic;
            subscribeToTopic(initTopic);

            log("Connection established to " + brokerHostName + " on topic " + initTopic);

            // Save start time
            // 节省开始时间
            mStartTime = System.currentTimeMillis();
            // Star the keep-alives
            startKeepAlives();
        }

        // Disconnect
        public void disconnect() {
            try {
                stopKeepAlives();
                mqttClient.disconnect();
            } catch (MqttPersistenceException e) {
                log("MqttException" + (e.getMessage() != null ? e.getMessage() : " NULL"), e);
            }
        }

        /*
         * Send a request to the message broker to be sent messages published with
         *  the specified topic name. Wildcards are allowed.
         *  向消息代理发送一个请求，以发送与发布的消息
         *  指定的主题名称。通配符是允许的。
         */
        private void subscribeToTopic(String topicName) throws MqttException {

            if ((mqttClient == null) || (mqttClient.isConnected() == false)) {
                // quick sanity check - don't try and subscribe if we don't have
                //  a connection
                // 快速检查——如果我们没有的话，不要尝试和订阅。
                //连接
                log("Connection error" + "No connection");
            } else {
                String[] topics = {topicName};
                mqttClient.subscribe(topics, MQTT_QUALITIES_OF_SERVICE);
            }
        }

        /*
         * Sends a message to the message broker, requesting that it be published
         *  to the specified topic.
         *  向消息代理结束一条消息，请求它被发布
         *  到指定的主题。
         */
        private void publishToTopic(String topicName, String message) throws MqttException {
            if ((mqttClient == null) || (mqttClient.isConnected() == false)) {
                // quick sanity check - don't try and publish if we don't have
                //  a connection
                log("No connection to public to");
            } else {
                mqttClient.publish(topicName,
                        message.getBytes(),
                        MQTT_QUALITY_OF_SERVICE,
                        MQTT_RETAINED_PUBLISH);
            }
        }

        /*
         * Called if the application loses it's connection to the message broker.
         * 如果应用程序失去了它与message broker的连接，就调用它。
         */
        public void connectionLost() throws Exception {
            log("Loss of connection" + "connection downed");
            stopKeepAlives();
            // null itself
            mConnection = null;
            if (isNetworkAvailable() == true) {
                reconnectIfNecessary();
            }
        }

		/*
         * Called when we receive a message from the message broker.
         * 当我们从message broker接收消息时调用
		 */

        public void publishArrived(String topicName, byte[] payload, int qos, boolean retained) {
            // Show a notification
            String s = new String(payload);
            showNotification(s);
            log("Got message: " + s);
        }

        public void sendKeepAlive() throws MqttException {
            log("Sending keep alive");
            // publish to a keep-alive topic
            publishToTopic(MQTT_CLIENT_ID + "/keepalive", mPrefs.getString(PREF_DEVICE_ID, ""));
        }
    }

}